#include <iostream>
#include "httplib.h"
#include "contact_exception.hh"
#include "add_contacts.pb.h"

using namespace httplib;
using namespace add_contacts;

#define CONTACTS_HOST "43.143.235.128"
#define CONTACTS_PORT 8085

void add_contact();

void menu()
{
    std::cout << "-----------------------------------------------------" << std::endl
              << "--------------- 请选择对通讯录的操作 ----------------" << std::endl
              << "------------------ 1、新增联系⼈ --------------------" << std::endl
              << "------------------ 2、删除联系⼈ --------------------" << std::endl
              << "------------------ 3、查看联系⼈列表 ----------------" << std::endl
              << "------------------ 4、查看联系⼈详细信息 ------------" << std::endl
              << "------------------ 0、退出 --------------------------" << std::endl
              << "-----------------------------------------------------" << std::endl;
}

int main()
{
    // 枚举显示几个选项，方便后续变更
    enum OPTION
    {
        EXIT = 0,
        ADD,
        DEL,
        FIND_ALL,
        FIND_ONE
    };
    // 使用循环模式显示菜单
    while (true)
    {
        menu();
        std::cout << "请输入选项对通讯录进行操作:";
        int choose;
        std::cin >> choose;
        std::cin.ignore(256, '\n');
        try
        {
            switch (choose)
            {
            case OPTION::EXIT:
                std::cout << "已退出" << std::endl;
                return 0;
            case 1:
                add_contact();
                break;
            case OPTION::DEL:
                break;
            case OPTION::FIND_ALL:
                break;
            case OPTION::FIND_ONE:
                break;
            default:
                std::cout << "选择有误,请重新选择!" << std::endl;
                break;
            }
        } // 如果有错误信息会直接catch到此处
        catch (const contact_exception &message)
        {
            std::cout << "-----操作通讯录时出现异常" << std::endl
                      << "-----异常信息" << message.what() << std::endl;
        }
    }
    return 0;
}

//  对应add_contacts()函数中新增联系人信息的具体方法
void build_add_contacts(add_contacts::add_contacts_request *rep)
{
    std::cout << "-------------新增联系⼈-------------" << std::endl;
    std::cout << "请输入要添加的联系人姓名: ";
    std::string name;
    std::getline(std::cin, name);
    rep->set_name(name);
    std::cout << "请输入联系人年龄: ";
    int age;
    std::cin >> age;
    rep->set_age(age);
    std::cin.ignore(256, '\n');
    /**
     * cin.ignore(a,ch)函数的解释
     * 它表示从输入流 cin 中提取字符，提取的字符被忽略，不被使用
     * 而每抛弃一个字符，它都要进行计数和比较字符
     * 如果计数值达到 a 或者被抛弃的字符是 ch ，则cin.ignore() 函数执行终止；否则，它继续等待。
     */
    for (int i = 0;; i++)
    {
        std::cout << "请输入联系人电话: " << i + 1 << "(只输入回车完成电话的新增): ";
        std::string number;
        std::getline(std::cin, number);
        if (number.empty())
        {
            break;
        }
        add_contacts::add_contacts_request::Phone *phone = rep->add_phone();
        phone->set_number(number);

        std::cout << "请选择输入该电话的类型(1.移动电话 2.固定电话):";
        int type;
        std::cin >> type;
        std::cin.ignore(256, '\n'); // 也是清除cin中的\n
        switch (type)
        {
        /**
         * set_type()函数-->void set_type(::contacts::Phone_PhoneType value);
         * 该函数表示的就是设置phone的电话类型
         */
        case 1:
            phone->set_type(add_contacts::add_contacts_request_Phone_PhoneType::add_contacts_request_Phone_PhoneType_MP);
            break;
        case 2:
            phone->set_type(add_contacts::add_contacts_request_Phone_PhoneType::add_contacts_request_Phone_PhoneType_TEL);
            break;
        default:
            std::cout << "请正确选择!" << std::endl;
            break;
        }
    }
}

// ADD选项，表示新增一个联系人信息
void add_contact()
{
    // 构建一个client请求
    Client client(CONTACTS_HOST, CONTACTS_PORT);
    // 构造请求req
    add_contacts::add_contacts_request req;
    build_add_contacts(&req);

    // 序列化req
    std::string req_str;
    if (!req.SerializeToString(&req_str)) // 如果序列化失败
    {
        throw contact_exception("add_contacts_request失败!");
    }
    // 发起post调用
    auto res = client.Post("/contacts/add", req_str, "application/protobuf");
    if (!res) // 请求post失败
    {
        std::string err_desc;
        err_desc.append("/contacts/add链接失败!错误信息: ")
            .append(httplib::to_string(res.error())); // 此处添加httplib库中对应的错误信息
        throw contact_exception(err_desc);
    }
    // 接收rep,反序列化rep
    add_contacts::add_contacts_response rep;
    bool parse = rep.ParseFromString(res->body);
    if (res->status != 200 && !parse)
    {
        std::string err_desc;
        err_desc.append("/contacts/add 调用失败")
            .append(std::to_string(res->status))
            .append("(")
            .append(res->reason)
            .append(")");
        throw contact_exception(err_desc);
    }
    else if (res->status != 200)
    {
        std::string err_desc;
        err_desc.append("/contacts/add 调用失败")
            .append(std::to_string(res->status))
            .append("(")
            .append(res->reason)
            .append("),错误原因:")
            .append(rep.error_desc());
        throw contact_exception(err_desc);
    }
    else if (!rep.success())
    {
        std::string err_desc;
        err_desc.append("/contacts/add 结果异常")
            .append("异常原因:")
            .append(rep.error_desc());
        throw contact_exception(err_desc);
    }
    // 读取数据打印
    std::cout << "新增联系人成功,联系人ID:" << rep.uid() << std::endl;
}
